package com.gitee.wsl.base

import com.gitee.wsl.func.consumer.BiConsumer
import com.gitee.wsl.func.BiFunction
import com.gitee.wsl.func.predicate.BiPredicate

interface Both<A, B> {
    /**
     * Applies the `mapper` Func with this pair of two things as arguments.
     *
     * @throws NullPointerException if `mapper` is null
     */
    fun <T> andThen(mapper: BiFunction<in A, in B, T>): T

    /**
     * If the pair [matches()][.matches] `condition`, returns a [BiOptional] containing
     * the pair, or else returns empty.
     *
     * @throws NullPointerException if `condition` is null, or if `condition` matches but
     * either value in this pair is null
     */
    fun filter(condition: BiPredicate<in A, in B>): BiOptional<A, B> {
        return andThen { a: A, b: B ->
            if (condition.test(
                    a,
                    b
                )
            ) BiOptional.of(a, b) else BiOptional.empty()
        }
    }

    /**
     * If the pair matches `predicate`, it's skipped (returns empty).
     *
     * @throws NullPointerException if `predicate` is null
     * @since 6.6
     */
    /*fun skipIf(predicate: BiPredicate<in A, in B>): BiOptional<A, B> {
        return filter(predicate.negate())
    }*/

    /**
     * Returns true if the pair matches `condition`.
     *
     * @throws NullPointerException if `condition` is null
     */
    fun matches(condition: BiPredicate<in A, in B>): Boolean {
        return andThen { t: A, u: B ->
            condition.test(t, u)
        }
    }

    /**
     * Invokes `consumer` with this pair and returns this object as is.
     *
     * @throws NullPointerException if `consumer` is null
     */
    fun peek(consumer: BiConsumer<in A, in B>): Both<A, B> {
        return andThen { a: A, b: B ->
            consumer.accept(a, b)
            this
        }
    }

    /**
     * Returns an immutable [Map.Entry] holding the pair of values.
     *
     * @since 6.5
     */
    /*fun toEntry(): Map.Entry<A, B>? {
        return andThen<Map.Entry<A, B>>(BiFunction<A, B, Map.Entry<A, B>> { AbstractMap.SimpleImmutableEntry() })
    }*/

    companion object {
        /**
         * Returns an instance with both `a` and `b`.
         *
         * @since 5.8
         */
        fun <A, B> of(a: A, b: B): Both<A, B> {
            return BiOptional.Present(a, b)
        }
    }
}